#ifndef PUDGE_GRFX_FONT_HPP
#define PUDGE_GRFX_FONT_HPP

#include <pudge/grfx/image.hpp>

#include <ft2build.h>
#include FT_FREETYPE_H
#include <pudge/grfx/texture.hpp>

namespace pudge { namespace grfx {

	struct font
	{
		typedef std::string string_type;
		typedef color<ALPHA,unsigned char> color_type;
		typedef image<color_type> image_type;
		
		struct texture_traits
		{
			typedef color_type color_type;
			typedef REPEAT_TEXTURE wrap_type;
		};

		struct glyph
		{
			float 	s			// texcoord s 
				,	t			// texcoord t
				,	tex_width	// texcoord width
				,	tex_height;	// texcoord height

			int		advance		// Advance in pixels
				,	width		// Width in pixels	
				,	height		// Height in pixels
				,	top			// Top ruler
				,	left;		// Left ruler
		} glyphs_ [100];

		int 	x_min
			,	x_max
			,	y_min
			,	y_max
			,	bbw
			,	bbh
			,	columns
			,	rows;

		image_type * image_;
		image_type & image() { return *image_; }

		glyph & operator [] (const char c)
		{
			const int i = c - 32;
			return glyphs_[i];
		}

		~font()
		{
			delete image_;
		}

		font
			(	string_type const& fname
			,	const int point
			,	const int resolution = 96 )
		{
			FT_Library lib;
			if(FT_Init_FreeType(&lib) != 0)
				throw -1;

			FT_Face face;

			int error = FT_New_Face
				(	lib
				,	fname.c_str()
				,	0
				,	&face );
			if(error != 0)
			{
				FT_Done_FreeType(lib);
				throw -1;
			}
			
			error = FT_Set_Char_Size
				(	face			// handle to face
				,	0				// char_width in 1/64 point
				,	point << 6		// char_height in 1/64 point
				,	0				// horizontal device resolution
				,	resolution );	// vertical device resolution
			if(error != 0 || !FT_IS_SCALABLE(face))
			{
				FT_Done_Face(face);
				FT_Done_FreeType(lib);
				throw -1;
			}

			const FT_Fixed x_scale = face->size->metrics.x_scale;
			const FT_Fixed y_scale = face->size->metrics.y_scale;

			FT_BBox & bbox = face->bbox;

			x_min = (FT_MulFix(bbox.xMin, x_scale) >> 6) - 1;
			x_max = (FT_MulFix(bbox.xMax, x_scale) >> 6) + 1;
			y_min = (FT_MulFix(bbox.yMin, y_scale) >> 6) - 1;
			y_max = (FT_MulFix(bbox.yMax, y_scale) >> 6) + 1;
			
			bbw = x_max - x_min;
			bbh = y_max - y_min;
			
			columns 	= 1024 / bbw;
			rows 		= 100 / columns + 1;

			image_ = new image_type(1024, bbh * rows);
			image_->clear(color_type(0));

			for(int i = 0; i < 95; ++i)
			{
				char c = (char)(i + 32);
				int glyph_index = FT_Get_Char_Index(face, c);

				error = FT_Load_Glyph
					(	face
					,	glyph_index
					,	FT_LOAD_DEFAULT );
				if(error != 0)
				{
					delete image_;
					FT_Done_Face(face);
					FT_Done_FreeType(lib);
					throw -1;
				}

				error = FT_Render_Glyph
					(	face->glyph
					,	FT_RENDER_MODE_NORMAL );
				if(error != 0)
				{
					delete image_;
					FT_Done_Face(face);
					FT_Done_FreeType(lib);
					throw -1;
				}

				const int row = i / columns;
				const int col = i % columns;
				
				const int xpen = bbw * col;
				const int ypen 
					= 	(bbh * (row + 1) - 1) 
					+ 	face->glyph->bitmap_top 
					-	y_max;

				copy_glyph(xpen,ypen,face,*image_,glyphs_[i]);
			}

			FT_Done_Face(face);
			FT_Done_FreeType(lib);
		}

		void copy_glyph(const int xpen, const int ypen, FT_Face & f, image_type & i, glyph & g)
		{
			g.width 		= f->glyph->bitmap.pitch;
			g.height 		= f->glyph->bitmap.rows;
			g.advance		= f->glyph->metrics.horiAdvance >> 6;
			g.top			= f->glyph->bitmap_top;
			g.left			= f->glyph->bitmap_left;

			g.s				= (float)xpen / i.width();
			g.t				= (float)(ypen+1) / i.height();
			g.tex_width 	= (float)g.width / i.width();
			g.tex_height 	= (float)g.height / i.height();

			
			for(int r = 0; r < g.height; ++r)
			{
				const int bitmap_offset = r * g.width;
				const int image_offset 	= (ypen - r) * i.width() + xpen;
				
				for(int c = 0; c < g.width; ++c)
					i.colors()[image_offset + c].a = f->glyph->bitmap.buffer[bitmap_offset + c];
			}
		}
	};

	template<class GCon> void font_test(GCon & gcon)
	{
		font f(std::string("\\Windows\\Fonts\\arial.ttf"),16);
		font::image_type & i = f.image();

		texture2d<font::texture_traits> tcon(i.width(), i.height());
		cache_texture(tcon, i.colors());

		//glShadeModel(GL_FLAT);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		bind_texture(gcon,tcon,0);

		glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.9f, -0.9f, 0.0f);
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.9f, 0.9f, 0.0f);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(0.9f, 0.9f, 0.0f);
		glTexCoord2f(1.0f, 0.0f); glVertex3f(0.9f, -0.9f, 0.0f);

		glEnd();
		glFlush();
		glDisable(GL_TEXTURE_2D);

		decache_texture(gcon,tcon);
	}
}}
#endif